/*
 * Copyright 2010 (c) Dou Yongwang (douyongwang@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef SJSON_TEMPLATE_H
#define SJSON_TEMPLATE_H

// INCLUDES
#include <e32std.h>
#include <e32base.h>
#include <e32hashtab.h>
#include <badesca.h>    // for CPtrC8Array
#include <sjson.h>
//#include "FastDelegate.h"

using namespace sjson;
//using namespace fastdelegate;

namespace sjson_template {

class CSection;
class CScopedContext;

typedef CJSONValue CContext;
typedef CJSONValue CDictValue;

// CLASS DECLARATION

/////////////////////////
// MTemplateRenderCallback
class MTemplateRenderCallback
	{
public:
	virtual void HandleTemplateRenderedStringL(const TDesC8& aRenderedString) = 0;
	};

////////////
// MStatement
class MStatement
	{
public:
	virtual ~MStatement() {}
	virtual void ExecuteL(CScopedContext& context, MTemplateRenderCallback& callback) = 0;
	};

/////////////////
// CLiteralStatement
class CLiteralStatement : public CBase, public MStatement
	{
public:
	static CLiteralStatement* NewL(const TDesC8& aPlainText);

private:
	virtual void ExecuteL(CScopedContext& context, MTemplateRenderCallback& callback);

private:
	CLiteralStatement(const TDesC8& aPlainText);
private:
	TPtrC8 iPlainText;
	};

/////////////////////
// CSectionStatement
class CSectionStatement : public CBase, public MStatement
	{
public:
	static CSectionStatement* NewL(CSection& aSection);
private:
	virtual void ExecuteL(CScopedContext& context, MTemplateRenderCallback& callback);
private:
	CSectionStatement(CSection& aSection);
private:
	CSection& iBlock;
	};
/////////////////////
// CRepeatedSectionStatement
class CRepeatedSectionStatement : public CBase, public MStatement
	{
public:
	static CRepeatedSectionStatement* NewL(CSection& aSection);
private:
	virtual void ExecuteL(CScopedContext& context, MTemplateRenderCallback& callback);
private:
	CRepeatedSectionStatement(CSection& aSection);
private:
	CSection& iBlock;
	};
/////////////////
// CSubstitutionStatement
class CSubstitutionStatement : public CBase, public MStatement
	{
public:
	static CSubstitutionStatement* NewL(const TDesC8& aName);

private:
	virtual void ExecuteL(CScopedContext& context, MTemplateRenderCallback& callback);

private:
	CSubstitutionStatement(const TDesC8& aName);
private:
	TPtrC8 iName;
	};
/////////////////
// CIndexStatement
class CIndexStatement : public CBase, public MStatement
	{
public:
	static CIndexStatement* NewL();

private:
	virtual void ExecuteL(CScopedContext& context, MTemplateRenderCallback& callback);

private:
	CIndexStatement();
private:
	};

////////////////////////////////
// CScopedContext
//
class CScopedContext : public CBase
	{
public:
	struct TItem
		{
		TItem(CContext* aContext, TInt aIndex) : iContext(aContext), iIndex(aIndex) {}
		CContext* iContext;
		TInt iIndex;
		};
typedef RArray<TItem> RItemArray;

	// Constructors and destructor
	~CScopedContext();
	static CScopedContext* NewL(CContext* aContext);
	static CScopedContext* NewLC(CContext* aContext);

public:
	CContext* PushSectionL(const TDesC8& aName);
	void Pop();
	TBool NextL();
	CContext* CursorValue();
	CDictValue* LookupL(const TDesC8& aName);
	const TItem& TopItem() const;

private:
	CScopedContext();
	void ConstructL(CContext* aContext);

private:
	RItemArray iStack;
	};


////////////////////////
// CSection
typedef RPointerArray<MStatement> RClauseArray;

class CSection : public CBase
	{
public:
	// Constructors and destructor
	~CSection();
	static CSection* NewL(const TDesC8& section_name, CSection* aParent=NULL);
	static CSection* NewLC(const TDesC8& section_name, CSection* aParent=NULL);

public:
	CSection* Parent();
	TPtrC8 SectionName();
	RClauseArray* Statements(const TDesC8& aSectionName = KNullDesC8);
	void NewClauseL(const TDesC8& aClauseName);
	void AppendL(MStatement* aStatement);
	void AppendL(const TDesC8& aPlainText);

private:
	CSection();
	void ConstructL(const TDesC8& section_name, CSection* aParent=NULL);
	void CleanMap();
//	void SetParent(CSection* aParent);

private:
	HBufC8* iSectionName;
	RClauseArray* iCurrentClause;
	typedef RPtrHashMap<TDesC8, RClauseArray> RClauseMap;
	typedef TPtrHashMapIter<TDesC8, RClauseArray> RClauseMapIter;
	RClauseMap iStatements;
	CSection* iParent;
	};

inline CSection* CSection::Parent()
	{ return iParent; }
//inline void CSection::SetParent(CSection* aParent)
//	{ iParent = aParent; }

/**
 *  CTemplate
 *
 */
class CTemplate : public CBase, public MTemplateRenderCallback
	{
public:
	// Constructors and destructor
	~CTemplate();
	static CTemplate* NewL(const TDesC8& aTemplateStr);
	static CTemplate* NewLC(const TDesC8& aTemplateStr);
	static void _Execute(RClauseArray* aStatements, CScopedContext* aContext, MTemplateRenderCallback& aCallBack);

public:
	void SetCallback(MTemplateRenderCallback* aCallback);
	void RenderL(CContext* aRootContext, MTemplateRenderCallback* aCallback);
	void ExpandL(const TDesC8& aJsonData);
	TPtrC8 Result() const;

private:
	// from MTemplateRenderCallback
	virtual void HandleTemplateRenderedStringL(const TDesC8& aRenderedString);

private:
	CTemplate();
	void ConstructL(const TDesC8& aTemplateStr);
	CSection* _CompileL(const TDesC8& aTemplateStr);
	/**
	 * find string positions enclosed with meta chars.
	 * @param aData the input data
	 * @param aLen TInt holds the length of the matched string after return.
	 * @return return position of iMetaLeft.
	 */
	TInt _MatchToken(const TDesC8& aData, TPtrC8& aTokenMatch);
	/**
	 * match a section
	 * @param aToken input token
	 * @param aRepeated output indicates whether it's a repeated section or not
	 * @return section name
	 */
	TPtrC8 _MatchSection(const TDesC8& aToken, TBool& aRepeated);

	TInt _MatchSimpleKeywords(const TDesC8& aToken);

//	void _Execute(RClauseArray* aStatements, CScopedContext* aContext, MTemplateRenderCallback& aCallBack);
	void _NewSectionL(TBool aRepeated, const TDesC8& aSectionName, CSection* aParent=NULL);

	typedef void (CTemplate::*FnDoSection)(CSection* aBlock, CScopedContext* aContext, MTemplateRenderCallback& aCallBack);

	static void SplitL(CPtrC8Array& aParts, const TDesC8& aToken, TUint8 aSepChar = '|');

private:
	typedef RPointerArray<CSection> RSectionArray;
	TUint8 iFormatChar;
	TPtrC8 iDefaultFormatter;
	TBuf8<20> iMeta;
	TPtrC8 iMetaLeft, iMetaRight;
	CSection* iRootBlock;
	CSection* iCurrentBlock;
	RSectionArray iStack;
	MTemplateRenderCallback* iCallback;
	HBufC8* iResult;
	};

inline void CTemplate::SetCallback(MTemplateRenderCallback* aCallback)
	{ iCallback = aCallback; }

} // namespace sjson_template

#endif // SJSON_TEMPLATE_H
